
package com.vanda.db.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;

import com.vanda.db.exception.MappingException;
import com.vanda.db.exception.ParseException;
import com.vanda.db.tag.Tag;

/**
 * 解析XQL配置xml内容<br>
 * 
 * @author 王瑛<br>
 * @version Version 1.00<br>
 */
public class XMLParser {
	/** 空白符定义 */
	public static String SPACE = "    ";
	/** 保存xql与解析后内容的对应关系 */
	private static Map xqlMap = new HashMap(); 
	
	/**
	 * 将xql的{}和[]组合转成tag标签<p>
	 * 例如：
	 * {	and	invai_code=[condition.invoiceCode]	}
	 * 转换后：
	 * <logic:notEmpty property="condition.invoiceCode">
	 * 		and	invai_code=[condition.invoiceCode]
	 * </logic:notEmpty>
	 * @param xql
	 * @return
	 */
	public static String convert(String xql) throws MappingException{
		int index = xql.indexOf(Tag.EMPTY_FLAG[0]);
		if(index < 0){
			return xql;
		}
		
		StringBuffer buffer = new StringBuffer();
		
		while(index >= 0){
			buffer.append(xql.substring(0,index));
			
			int tail = xql.indexOf(Tag.EMPTY_FLAG[1]);
			
			if(tail < 0){
				throw new MappingException("No "+Tag.EMPTY_FLAG[1]);
			}
			String emptyContent = xql.substring(index+1,tail);
			
			String property = getProperty(emptyContent);
			
			buffer.append("<logic:notEmpty property=\"")
				  .append(property).append("\">\n")
				  .append(SPACE).append(emptyContent)
				  .append("\n</logic:notEmpty>");
			
			xql = xql.substring(tail+1,xql.length());
			index = xql.indexOf(Tag.EMPTY_FLAG[0]);
		}
		
		buffer.append(xql);
		return buffer.toString();
	}
	
	/**
	 * 取得Property的内容
	 * @param xql
	 * @return
	 * @throws MappingException
	 */
	private static String getProperty(String xql) throws MappingException{
		int index = xql.indexOf(Tag.PROPERTY_FLAG[0]);
		
		if(index < 0){
			throw new MappingException("No "+Tag.PROPERTY_FLAG[0]);
		}
		
		int tail = xql.indexOf(Tag.PROPERTY_FLAG[1]);
		if(tail < 0){
			throw new MappingException("No "+Tag.PROPERTY_FLAG[1]);
		}
		
		return xql.substring(index+1,tail);
	}
	
	/**
	 * 解析xql，按顺序将其分解成标签部分和非标签部分<p>
	 * 例如：<br>
	 * select * from a<br> 
	 * where 1=1 <br>
	 * <logic:notEmpty property="a.field1"><br>
	 * 	and a.field = {a.field1}<br>
	 * </logic:notEmpty><br>
	 * order by a.id<br>
	 * 解析之后<br>
	 * str[0]:select * from a where 1=1<br>
	 * str[1]:<logic:notEmpty property="a.field1"> and a.field = {a.field1} </logic:notEmpty><br>
	 * str[2]:order by a.id<br>
	 * @param xql
	 * @return
	 */
	public static String[] parse(String xql) throws ParseException{
		String[] strs = (String[])xqlMap.get(xql);
		
		if(strs != null){
			return strs;
		}
				
		List lstStrs = new ArrayList();
		
		Position[] positions = getTagPosition(xql);
		if(positions.length == 0){
			lstStrs.add(xql);
			strs = (String[])lstStrs.toArray(new String[0]);
			xqlMap.put(xql,strs);
			
			return strs;
		}
		
		int wholeTag = 0;
		int start = 0;
		for(int i=0;i<positions.length;i++){
			if(i == 0){
				lstStrs.add(xql.substring(0,positions[0].pos));
			}
			if(positions[i].head == true){
				wholeTag ++;
			}else{
				wholeTag --;
			}
			
			//如果是完整Tag
			if(wholeTag == 0){
				String tagTail = xql.substring(positions[i].pos);
				int index = tagTail.indexOf(">")+1;
				
				lstStrs.add(xql.substring(positions[start].pos,positions[i].pos+index));
				if(i+1 < positions.length){
					lstStrs.add(xql.substring(positions[i].pos+index,positions[i+1].pos));
				}else{
					lstStrs.add(xql.substring(positions[i].pos+index));
				}
				start = i+1;	
			}
		}

		strs = (String[])lstStrs.toArray(new String[0]);
		xqlMap.put(xql,strs);
		
		return strs;
	}
	
	/**
	 * 取得标签的头部<p>
	 * 例如：<br>
	 * <logic:notEmpty property="a.field1"><br>
	 * 	and a.field = {a.field1}<br>
	 * </logic:notEmpty><br>
	 * 返回值：<logic:notEmpty property="a.field1">
	 * @param tag
	 * @return
	 */
	public static String getTagHead(String tag){	
		String head = null;
		
		int index = tag.indexOf(">");
		
		head = tag.substring(0,index+1);
		
		return head;
	}
	
	/**
	 * 取得标签的内容<p>
	 * 例如：<br>
	 * <logic:notEmpty property="a.field1"><br>
	 * 	and a.field = {a.field1}<br>
	 * </logic:notEmpty><br>
	 * 返回值：and a.field = {a.field1}
	 * @param tag
	 * @return
	 */
	public static String getTagContent(String tag){

		String content = null;
		
		String tagTail = Tag.TAG_TAIL + getTagName(getTagHead(tag));
		
		int start = tag.indexOf(">") + 1;
		
		int end = tag.lastIndexOf(tagTail);
		
		content = tag.substring(start,end);
		
		return content;
	}
	
	/**
	 * 取得标签名<p>
	 * 例如：<br>
	 * 标签头是<logic:notEmpty property="a.field1"><br>
	 * 返回notEmpty<br>
	 * @param tagHead 标签头
	 * @return
	 */
	public static String getTagName(String tagHead){
		return getTagName(tagHead,true);
	}
	
	/**
	 * 取得标签名<p>
	 * 例如：<br>
	 * 标签头是<logic:notEmpty property="a.field1"><br>
	 * 返回notEmpty<br>
	 * @param tagHead 标签头
	 * @param head 是否是标签头，true-标签头，false-标签尾
	 * @return
	 */
	public static String getTagName(String tag,boolean head){
		String name = "";
		
		String flag = Tag.TAG_HEAD;
		if(!head){
			flag = Tag.TAG_TAIL;
		}
		
		int index = tag.indexOf(flag);
		String substr = tag.substring(index+flag.length());
		
		int index1 = substr.indexOf(" ");
		int index2 = substr.indexOf(">");
		
		index = index1;
		if(index == -1 || index > index2){
			index = index2;
		}
		name = substr.substring(0,index).trim();
		
		return name;
	}
	
	/**
	 * 取得标签的属性集合<p>
	 * <logic:notEmpty property="a.field1" value="3"><br>
	 * 返回值：<br>
	 * key=property value="a.field1"<br>
	 * key=value    value="3"<br>
	 * @param tagHead
	 * @return
	 */
	public static HashMap getTagAttributes(String tagHead){
		HashMap attrs = new HashMap();
		
		StringTokenizer token = new StringTokenizer(tagHead,"=");
		String key = null;
		String value = null;
		
		int count = token.countTokens();
		for(int i=0;i<count;i++){
			String str = token.nextToken();
			
			if(i > 0){
				int index = str.indexOf(" ");
				if(index < 0){
					index = str.indexOf(">");
				}
				value = str.substring(0,index).trim();
				attrs.put(key,trim(value));
			}
			if(i != count -1){
				key = str.substring(str.lastIndexOf(" ")).trim();
			}
		}
		
		return attrs;
	}
	
	/**
	 * 去掉多余的双引号
	 * @param value
	 * @return
	 */
	private static String trim(String value){
		int start = 0;
		int end = value.length();
		
		if(value.charAt(0) == '\"'){
			start = 1;
		}
		if(value.charAt(end-1) == '\"'){
			end = end - 1;
		}
		return value.substring(start,end);
	}

	
	/**
	 * 标识xql标签的位置
	 * @param xql
	 * @return
	 */
	private static Position[] getTagPosition(String xql) throws ParseException{
		List lstPos = new ArrayList();
		int index = xql.indexOf(Tag.TAG_HEAD);
		boolean head = true;
		int offset = 0;
		//构建标签栈，校验用
		Stack stack = new Stack();
		String xqlclone = new String(xql);
		
		while(index >= 0){
			Position pos = new Position();
			pos.pos = offset+index;
			pos.head = head;
			if(head){
				pos.name = getTagName(xqlclone.substring(pos.pos));
			}else{
				pos.name = getTagName(xqlclone.substring(pos.pos),false);
			}

			lstPos.add(pos);
			
			//如果是标签头，入栈操作
			if(pos.head){
				stack.push(pos.name);
			}else{
				String tagName = (String)stack.peek();
				if(tagName.equals(pos.name)){
					stack.pop();
				}else{
					throw new ParseException("Tag ["+tagName+"] has not end-tag!");
				}
			}
			
			if(head){
				offset += index + Tag.TAG_HEAD.length();
				xql = xql.substring(index+Tag.TAG_HEAD.length());
			}else{
				offset += index + Tag.TAG_TAIL.length();
				xql = xql.substring(index+Tag.TAG_TAIL.length());
			}
			
			head = true;
			index = xql.indexOf(Tag.TAG_HEAD);
			int index1 = xql.indexOf(Tag.TAG_TAIL);
			
			if(index1 != -1 && (index == -1 || index > index1)){
				head = false;
				index = index1;
			}
			
		}
		
		//检查stack是否还有标签，如果有，则报错
		String tagName = "";
		while(!stack.empty()){
			tagName += (String)stack.pop()+",";
		}
		if(!tagName.equals("")){
			tagName = tagName.substring(0,tagName.length()-1);
			throw new ParseException("Tag ["+tagName+"] has not end-tag!");
		}
		
		return (Position[])lstPos.toArray(new Position[0]);
	}
}
class Position{
	public int pos;
	public String name;
	public boolean head;
}